#!/bin/bash
# 脚本解释器声明：使用 Bash 运行该脚本
# 定义模型配置文件路径（包含需要注释/解注释的模型条目）
CONFIG_MODELS_FILE="config/model/clip-vit/clip-vit-base-patch16_TALL20.yaml"
# 定义任务配置文件路径（与模型文件同步进行注释/解注释的对应任务条目）
CONFIG_TASKS_FILE="config/taskpool/CLIPVisionModelTaskPool/clip-vit-classification_TA20_B16.yaml"

# 定义所有模型名称列表（完整标识，需与模型配置文件中的条目一致）
CONTINUE_MOE="clip-vit-base-patch16-continue_cache_model_20task"

MODELS=(
    # 模型标识：ViT-B/16 在 SUN397 数据集上的适配条目
    "clip-vit-base-patch16_sun397"
    # 模型标识：ViT-B/16 在 Stanford Cars 数据集上的适配条目
    "clip-vit-base-patch16_stanford-cars"
    # 模型标识：ViT-B/16 在 RESISC45 数据集上的适配条目
    "clip-vit-base-patch16_resisc45"
    # 模型标识：ViT-B/16 在 EuroSAT 数据集上的适配条目
    "clip-vit-base-patch16_eurosat"
    # 模型标识：ViT-B/16 在 SVHN 数据集上的适配条目
    "clip-vit-base-patch16_svhn"
    # 模型标识：ViT-B/16 在 GTSRB 数据集上的适配条目
    "clip-vit-base-patch16_gtsrb"
    # 模型标识：ViT-B/16 在 MNIST 数据集上的适配条目
    "clip-vit-base-patch16_mnist"
    # 模型标识：ViT-B/16 在 DTD 数据集上的适配条目
    "clip-vit-base-patch16_dtd"
    # 模型标识：ViT-B/16 在 Oxford Flowers 102 数据集上的适配条目
    "clip-vit-base-patch16_oxford_flowers102"
    # 模型标识：ViT-B/16 在 PCAM 数据集上的适配条目
    "clip-vit-base-patch16_pcam"
    # 模型标识：ViT-B/16 在 FER2013 数据集上的适配条目
    "clip-vit-base-patch16_fer2013"
    # 模型标识：ViT-B/16 在 Oxford-IIIT Pet 数据集上的适配条目
    "clip-vit-base-patch16_oxford-iiit-pet"
    # 模型标识：ViT-B/16 在 STL10 数据集上的适配条目
    "clip-vit-base-patch16_stl10"
    # 模型标识：ViT-B/16 在 CIFAR100 数据集上的适配条目
    "clip-vit-base-patch16_cifar100"
    # 模型标识：ViT-B/16 在 CIFAR10 数据集上的适配条目
    "clip-vit-base-patch16_cifar10"
    # 模型标识：ViT-B/16 在 Food101 数据集上的适配条目
    "clip-vit-base-patch16_food101"
    # 模型标识：ViT-B/16 在 Fashion MNIST 数据集上的适配条目
    "clip-vit-base-patch16_fashion_mnist"
    # 模型标识：ViT-B/16 在 EMNIST Letters 数据集上的适配条目
    "clip-vit-base-patch16_emnist_letters"
    # 模型标识：ViT-B/16 在 KMNIST 数据集上的适配条目
    "clip-vit-base-patch16_kmnist"
    # 模型标识：ViT-B/16 在 Rendered SST2 数据集上的适配条目
    "clip-vit-base-patch16_rendered-sst2"
)

# 定义任务名称列表（与 MODELS 一一对应，用于同步任务配置文件）
TASKS=(
    # 任务名：SUN397 场景分类
    "sun397"
    # 任务名：Stanford Cars 汽车分类
    "stanford-cars"
    # 任务名：RESISC45 遥感图像分类
    "resisc45"
    # 任务名：EuroSAT 遥感卫星图像分类
    "eurosat"
    # 任务名：SVHN 街景门牌数字识别
    "svhn"
    # 任务名：GTSRB 交通标志识别
    "gtsrb"
    # 任务名：MNIST 手写数字识别
    "mnist"
    # 任务名：DTD 纹理识别
    "dtd"
    # 任务名：Oxford Flowers 102 花卉分类
    "oxford_flowers102"
    # 任务名：PCAM 癌症图像分类
    "pcam"
    # 任务名：FER2013 表情识别
    "fer2013"
    # 任务名：Oxford-IIIT Pet 宠物分类
    "oxford-iiit-pet"
    # 任务名：STL10 无标签图像分类
    "stl10"
    # 任务名：CIFAR100 细粒度图像分类
    "cifar100"
    # 任务名：CIFAR10 图像分类
    "cifar10"
    # 任务名：Food101 食物分类
    "food101"
    # 任务名：Fashion MNIST 时尚商品分类
    "fashion_mnist"
    # 任务名：EMNIST Letters 字母分类
    "emnist_letters"
    # 任务名：KMNIST 手写数字分类
    "kmnist"
    # 任务名：Rendered SST2 情感分类
    "rendered-sst2"
)

    #   - sun397
    #   - stanford-cars
    #   - resisc45
    #   - eurosat
    #   - svhn
    #   - gtsrb
    #   - mnist
    #   - dtd

# 示例注释块（保留）：用于直观展示配置文件中可能存在的条目形态
#   - continue_cache_model
  # - clip-vit-base-patch32_sun397
#   - clip-vit-base-patch32_stanford-cars
  # - clip-vit-base-patch32_resisc45
  # - clip-vit-base-patch32_eurosat
  # - clip-vit-base-patch32_svhn
  # - clip-vit-base-patch32_gtsrb
  # - clip-vit-base-patch32_mnist
  # - clip-vit-base-patch32_dtd

# 预留变量：定义需要被注释掉的列表（当前未使用）
COMMENTED=()

# 状态变量：记录上次被解开的模型名称（用于在下一次解开前先注释）
LAST_ENABLED=""

# 工具函数：根据模型名称找到对应的任务名称（按 MODELS/TASKS 数组索引映射）
get_task_by_model() {
    # 接收模型标识参数
    local model="$1"
    # 遍历 MODELS 数组，通过索引查找匹配项
    for idx in "${!MODELS[@]}"; do
        # 若当前位置的模型名称与输入匹配，则返回同索引的任务名称
        if [[ "${MODELS[$idx]}" == "$model" ]]; then
            echo "${TASKS[$idx]}"
            return 0
        fi
    done
    # 未找到匹配时返回空字符串
    echo ""
}

# 基础命令：定义一次实验运行的 CLI 命令（包含方法、设备、模型池、任务池和结果保存路径）
# - method：使用 s2_moe_upscaling 方法
# - method.device：使用 CUDA 设备
# - method.gate_k / method.k：MoE 门控与专家数相关参数
# - modelpool / taskpool：分别指定模型池与任务池配置名称
# - report_save_path：结果保存的 JSON 路径
CMD_BASE='HYDRA_FULL_ERROR=1 CUDA_VISIBLE_DEVICES=4 fusion_bench method=s2_moe/s2_moe_upscaling method.sv_reduction=0.05 method.save_model_path=save_models/clip-vit-base-patch16_20_tasks method.device=cuda method.gate_k=16 method.k=32 modelpool=CLIPVisionModelPool/clip-vit-base-patch16_TALL20 taskpool=CLIPVisionModelTaskPool/clip-vit-classification_TA20_B16'

# ==============================
# 🌀 随机实验循环
# ==============================
for j in $(seq 1 10); do
    # 外层循环标记与状态重置

    rm save_models/clip-vit-base-patch16_20_tasks/model_full.pth

    echo "=============================="
    echo "🔸 外层第 $j 次循环"
    echo "=============================="
    LAST_ENABLED=""

    # 注释掉 CONTINUE_MOE 条目（外层循环开始时先关闭缓存模型）
    echo "🔧 注释 CONTINUE_MOE 条目: $CONTINUE_MOE"
    sed -E -i "s|^([[:space:]]*)- *$CONTINUE_MOE|\1# - $CONTINUE_MOE|" "$CONFIG_MODELS_FILE"



    # —— 每次内层循环开始前：随机启用一个模型/任务，其余全部注释 ——
    # 随机选择一个索引
    idx1=$((RANDOM % ${#MODELS[@]}))

    echo "🎯 预启用模型(外层 $j): ${MODELS[$idx1]}"
    
    # 保存预启用的模型到数组中，供内层循环使用
    ENABLED_MODELS=("${MODELS[$idx1]}")
    
    # 遍历所有模型/任务，选中两项解注释，其余注释（模型与任务文件同步）
    for k in "${!MODELS[@]}"; do
        model="${MODELS[$k]}"
        task="${TASKS[$k]}"
        if [ "$k" -eq "$idx1" ]; then
            # 解注释模型
            sed -E -i "s|^([[:space:]]*)# *- *$model|\1- $model|" "$CONFIG_MODELS_FILE"
            # 解注释对应任务
            sed -E -i "s|^([[:space:]]*)# *- *$task|\1- $task|" "$CONFIG_TASKS_FILE"
        else
            # 注释模型
            sed -E -i "s|^([[:space:]]*)- *$model|\1# - $model|" "$CONFIG_MODELS_FILE"
            # 注释对应任务
            sed -E -i "s|^([[:space:]]*)- *$task|\1# - $task|" "$CONFIG_TASKS_FILE"
        fi
    done

    # —— 构建内层循环使用的候选模型池：排除外层预启用的两项 ——
    MODELS_POOL=()
    for m in "${MODELS[@]}"; do
        if [[ "$m" != "${MODELS[$idx1]}" ]]; then
            MODELS_POOL+=("$m")
        fi
    done
    # 剩余池：用于控制内层循环次数，确保每个候选条目被选择一次
    REMAINING_POOL=("${MODELS_POOL[@]}")
    i=1
    

    # 外层基准运行：使用 j_0.json 命名，避免与内层冲突
    BASE_REPORT_SAVE_PATH="outputs/ViT-B-16_20task/${j}/${j}_0.json"
    mkdir -p "$(dirname "$BASE_REPORT_SAVE_PATH")"
    eval "$CMD_BASE report_save_path=\"$BASE_REPORT_SAVE_PATH\""


    while [ ${#REMAINING_POOL[@]} -gt 0 ]; do
        # 输出分隔与当前轮次编号
        echo "=============================="
        echo "🔹 第 $i 次实验"
        echo "=============================="
        echo "📦 候选池(MODELS_POOL): ${MODELS_POOL[*]}"
        # 先运行一次基准命令（仅外层预启用两项 + continue_cache_model 生效）
        echo "🚀 先运行一次基准命令"
        # eval $CMD_BASE
        
        # 注释掉外层循环预启用的两个条目
        echo "🔧 注释外层预启用的两个条目: ${ENABLED_MODELS[*]}"
        for enabled_model in "${ENABLED_MODELS[@]}"; do
            # 注释模型条目（匹配格式：  - model_name）
            sed -E -i "s|^([[:space:]]*)- *$enabled_model|\1# - $enabled_model|" "$CONFIG_MODELS_FILE"
            # 同步注释对应任务条目
            # enabled_task=$(get_task_by_model "$enabled_model")
            # if [ -n "$enabled_task" ]; then
            #     sed -E -i "s|^([[:space:]]*)- *$enabled_task|\1# - $enabled_task|" "$CONFIG_TASKS_FILE"
            # fi
        done

        # 确保 continue_cache_model 始终处于解注释状态（模型配置文件）
        sed -E -i "s|^([[:space:]]*)# *- *$CONTINUE_MOE|\1- $CONTINUE_MOE|" "$CONFIG_MODELS_FILE"

    # ----------- 在解开新任务之前，先注释掉上次解开的任务（如果仍未注释） -----------
    # 若存在上次解开的模型（LAST_ENABLED 非空），先尝试在模型配置文件中将其重新注释
    if [ -n "$LAST_ENABLED" ]; then
        # 检查模型配置文件中是否存在未注释的对应条目
        if grep -E -q "^[[:space:]]*-[[:space:]]*$LAST_ENABLED" "$CONFIG_MODELS_FILE"; then
            echo "↩️ 注释上次解开的模型: $LAST_ENABLED"
            # 将未注释的模型条目替换为注释形式（保留缩进兼容）
            sed -E -i "s|^([[:space:]]*)-[[:space:]]*$LAST_ENABLED|\1# - $LAST_ENABLED|" "$CONFIG_MODELS_FILE"
        fi
        # # 同步：在任务配置文件中注释对应任务条目
        # LAST_TASK=$(get_task_by_model "$LAST_ENABLED")
        # if [ -n "$LAST_TASK" ] && grep -E -q "^[[:space:]]*-[[:space:]]*$LAST_TASK" "$CONFIG_TASKS_FILE"; then
        #     echo "↩️ 注释上次解开的任务: $LAST_TASK"
        #     # 将未注释的任务条目替换为注释形式（保留缩进兼容）
        #     sed -E -i "s|^[[:space:]]*-[[:space:]]*$LAST_TASK|# - $LAST_TASK|" "$CONFIG_TASKS_FILE"
        # fi
    fi
    # ----------- 随机取消注释 -----------
    # 找出当前注释掉的任务
    # 构建可解开的模型列表：在模型配置文件中处于注释状态的条目
    AVAILABLE_TO_ENABLE=()
    for t in "${REMAINING_POOL[@]}"; do
        # 仅当模型条目为注释状态（带 # -）才加入可解开列表
        if grep -E -q "^[[:space:]]*# *-[[:space:]]*$t" "$CONFIG_MODELS_FILE"; then
            AVAILABLE_TO_ENABLE+=("$t")
        fi
    done

    # 若没有任何可解开的模型条目，结束循环
    if [ ${#AVAILABLE_TO_ENABLE[@]} -eq 0 ]; then
        echo "没有可解开的任务，结束循环"
        break
    fi

    # 从可解开的模型列表中随机选择一个模型进行解注释
    RANDOM_ENABLE=${AVAILABLE_TO_ENABLE[$((RANDOM % ${#AVAILABLE_TO_ENABLE[@]}))]}
    echo "🧩 解开任务: $RANDOM_ENABLE"
    # 将模型配置文件中被注释的条目解注释为生效状态（保留原始缩进）
    # 说明：捕获行首缩进为分组1，在替换中用 \1 还原缩进
    # 只移除行首注释符及其后的空格，保留原始缩进与行内内容
    sed -E -i "s|^([[:space:]]*)#[[:space:]]*(-[[:space:]]*${RANDOM_ENABLE}.*)$|\1\2|" "$CONFIG_MODELS_FILE"
    # 同步：在任务配置文件中解注释对应任务条目
    ENABLE_TASK=$(get_task_by_model "$RANDOM_ENABLE")
    if [ -n "$ENABLE_TASK" ]; then
        # 只移除行首注释符及其后的空格，保留原始缩进与行内内容
        sed -E -i "s|^([[:space:]]*)#[[:space:]]*(-[[:space:]]*$ENABLE_TASK)|\1\2|" "$CONFIG_TASKS_FILE"
    fi
    # 记录本轮被解开的模型，用于下一轮开始时先行注释
    LAST_ENABLED="$RANDOM_ENABLE"

    # 已在本次解开前注释过上次解开的任务，这里不再随机注释其他任务

    # ----------- 运行实验 -----------
    # 执行定义好的命令，进行一次实验运行
    echo "🚀 运行命令"
    REPORT_SAVE_PATH="outputs/ViT-B-16_20task/${j}/${j}_${i}.json"
    mkdir -p "$(dirname "$REPORT_SAVE_PATH")"
    # 若 i 等于 1，则直接退出脚本
    # if [ "$i" -eq 1 ]; then
    #     echo "🔚 检测到 i==1，提前退出"
    #     exit 0
    # fi
    eval "$CMD_BASE report_save_path=\"$REPORT_SAVE_PATH\""

    # ----------- 结束后恢复状态 -----------
    # 为保证配置文件在每轮结束后回到基准状态，重新注释本轮解开的模型
    echo "🧹 将刚刚解开的模型重新注释回去: $RANDOM_ENABLE"
    sed -E -i "s|^([[:space:]]*)-[[:space:]]*$RANDOM_ENABLE|\1# - $RANDOM_ENABLE|" "$CONFIG_MODELS_FILE"

    # 同步：同时注释任务配置文件中的对应任务条目
    # RESTORE_TASK=$(get_task_by_model "$RANDOM_ENABLE")
    # if [ -n "$RESTORE_TASK" ]; then
    #     sed -E -i "s|^[[:space:]]*-[[:space:]]*$RESTORE_TASK|# - $RESTORE_TASK|" "$CONFIG_TASKS_FILE"
    # fi
    # 从剩余池中移除本次启用的条目，保证不重复选择
    NEW_REMAINING=()
    for t in "${REMAINING_POOL[@]}"; do
        if [[ "$t" != "$RANDOM_ENABLE" ]]; then
            NEW_REMAINING+=("$t")
        fi
    done
    REMAINING_POOL=("${NEW_REMAINING[@]}")
    i=$((i+1))
    done
done

# 所有循环结束后输出完成标记
echo "✅ 所有实验结束"
